#include "KeyRecommander.h"
#include "TcpConnection.h"

#include <sstream>

using namespace std;

/* KeyRecommander::KeyRecommander(string& query, const TcpConnectionPtr& conn) 
: _queryWord(query)
, _conn(conn)
{} */
void KeyRecommander::setQueryWord(string  query){
    _queryWord = query;
}
void KeyRecommander::setTcpConnectionPtr(const TcpConnectionPtr& conn){
    _conn = conn;
}
void KeyRecommander::execute() {
    queryIndexTable();
    response();
}

void KeyRecommander::queryIndexTable() {
    Dictionary* dict = Dictionary::createInstance();
    map<string, set<int>>& indexTable = dict->getIndexTable();
    int nBytes = nBytesCode(_queryWord[0]);
    if(nBytes != dict->getDictType()){
        if(nBytes == 1){
            dict->setData("dict_en.txt", "index_en.txt");
            dict->setDictType(1);
        }
        else{
            dict->setData("dict_zh.txt", "index_zh.txt");
            dict->setDictType(3);
        }
    }
    for(size_t i = 0; i < _queryWord.size(); ++i)
    {
        //cout << "begin" << i << endl;
        char alpha[4];
        nBytes = nBytesCode(_queryWord[i]);
        size_t j = 0;
        while(j < nBytes){
            alpha[j++] = _queryWord[i++];
        }
        --i;
        alpha[j] = '\0';
        string alphaString(alpha);
        //cout << alphaString << endl;
        auto it = indexTable.find(alphaString);
        if(it->second.empty()){
            continue;
        }
        statistic(it->second);
    }
}

void KeyRecommander::statistic(set<int>& iset) {
    Dictionary* dict = Dictionary::createInstance();
    vector<pair<string, int>>& dictVec = dict->getDict();
    for (auto it = iset.begin(); it != iset.end(); ++it) {
        //cout << dictVec[*it].first << endl;
        int distance = editDistance(_queryWord, dictVec[*it].first);
        //cout << distance << endl;
        if (_resultSet.find(dictVec[*it].first) == _resultSet.end()) {
            if (_resultQue.size() < 10) {
                _resultQue.push(MyResult(dictVec[*it].first, dictVec[*it].second, distance));
                _resultSet.insert(dictVec[*it].first);
            } else if (distance < _resultQue.top()._dist) {
                _resultSet.erase(_resultQue.top()._word);
                _resultQue.pop();
                _resultQue.push(MyResult(dictVec[*it].first, dictVec[*it].second, distance));
                _resultSet.insert(dictVec[*it].first);
            }
        }
    }
}

size_t KeyRecommander::nBytesCode(const char ch){
    if(ch & (1 << 7))
    {
        int nBytes = 1;
        for(int idx = 0; idx != 6; ++idx)
        {
            if(ch & (1 << (6 - idx)))
            {
                ++nBytes;	
            }
            else
                break;
        }
        return nBytes;
    }
    return 1;
}   

size_t KeyRecommander::length(const string &str){
    size_t ilen = 0;
    for(size_t idx = 0; idx != str.size(); ++idx)
    {
        int nBytes = nBytesCode(str[idx]);
        idx += (nBytes - 1);
        ++ilen;
    }
    return ilen;
}

int KeyRecommander::triple_min(const int &a, const int &b, const int &c)
{
    return a < b ? (a < c ? a : c) : (b < c ? b : c);
}

int KeyRecommander::editDistance(const string & lhs, const string &rhs){
    //计算最小编辑距离-包括处理中英文
    size_t lhs_len = length(lhs);
    size_t rhs_len = length(rhs);
    int editDist[lhs_len + 1][rhs_len + 1];
    for(size_t idx = 0; idx <= lhs_len; ++idx)
    {
        editDist[idx][0] = idx;
    }

    for(size_t idx = 0; idx <= rhs_len; ++idx)
    {
        editDist[0][idx] = idx;
    }

    string sublhs, subrhs;
    for(size_t dist_i = 1, lhs_idx = 0; dist_i <= lhs_len; ++dist_i, ++lhs_idx)
    {
        size_t nBytes = nBytesCode(lhs[lhs_idx]);
        sublhs = lhs.substr(lhs_idx, nBytes);
        lhs_idx += (nBytes - 1);

        for(size_t dist_j = 1, rhs_idx = 0; dist_j <= rhs_len; ++dist_j, ++rhs_idx)
        {
            nBytes = nBytesCode(rhs[rhs_idx]);
            subrhs = rhs.substr(rhs_idx, nBytes);
            rhs_idx += (nBytes - 1);
            if(sublhs == subrhs)
            {
                editDist[dist_i][dist_j] = editDist[dist_i - 1][dist_j - 1];
            }
            else
            {
                editDist[dist_i][dist_j] = triple_min(
                                                      editDist[dist_i][dist_j - 1] + 1,
                                                      editDist[dist_i - 1][dist_j] + 1,
                                                      editDist[dist_i - 1][dist_j - 1] + 1);
            }
        }
    }
    return editDist[lhs_len][rhs_len];
}



void KeyRecommander::response() {
    vector<string> resVec;
    while(!_resultQue.empty()){
        MyResult res = _resultQue.top();
        resVec.insert(resVec.begin(), res._word);
        _resultQue.pop(); 
    }
    ostringstream oss;
    for(auto & elem : resVec){
        oss << elem << " ";
    }
    oss << endl;
    // Send response to the client
    unique_ptr<RedisCache> pRedis(new RedisCache());
    if(!pRedis->connect("127.0.0.1",6379))
    {
        std::cerr << "connect error" << endl;
        return;
    }
    pRedis->keySet(_queryWord,oss.str());
    _conn->sendInLoop("rec\n");
    _conn->sendInLoop(oss.str());  
}

